home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
EnigmA Amiga Run 1996 March
/
EnigmA AMIGA RUN 05 (1996)(G.R. Edizioni)(IT)[!][issue 1996-03][Skylink CD IV].iso
/
earcd
/
editor
/
chktex.lha
/
chktex
/
source
/
FindErrs.c
< prev
next >
Wrap
C/C++ Source or Header
|
1996-01-25
|
15KB
|
703 lines
/*
* ChkTeX v1.2, error searching & report routines.
* Copyright (C) 1995-96 Jens T. Berger Thielemann
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
* Contact the author at:
* Jens Berger
* Spektrumvn. 4
* N-0666 Oslo
* Norway
* E-mail: <jensthi@ifi.uio.no>
*
*
*/
#include "ChkTeX.h"
/*
* Prints the status/conclusion after doing all the testing, including
* bracket stack status, math mode, etc.
*/
void PrintStatus(ULONG Lines)
{
ULONG Cnt;
struct CharInfo *ci;
if(ci = PopChar(&CharStack))
{
do
{
PrintError(ci->LineBuf, ci->Column - 1, 1, ci->Line,
emNoMatchC,
ci->Char);
free(ci);
} while(ci = PopChar(&CharStack));
}
if(MathMode)
{
PrintError(NULL, 0L, 0L, Lines,
emMathStillOn);
}
for(Cnt = 0L; Cnt < (NUMBRACKETS>>1); Cnt++)
{
if(Brackets[Cnt << 1] != Brackets[(Cnt << 1) + 1])
{
PrintError(NULL, 0L, 0L, Lines,
emNoMatchCC,
BrOrder[Cnt<<1], BrOrder[(Cnt<<1) + 1]);
}
}
if(Verbosity > vbSilent)
{
fprintf(OutputFile, "%ld error(s) printed; %ld warnings printed; %ld user suppressed warnings.\n",
ErrPrint, WarnPrint, UserSupp);
}
}
/*
* Searches the `Buf' for possible errors, and prints the errors. `Line'
* is supplied for error printing.
*/
BOOL FindErr(const STRPTR RealBuf, const ULONG Line)
{
STRPTR BufPtr, /* Read pointer in buffer */
CmdPtr, /* We'll have to copy each command out. */
PrePtr, /* Ptr to char in front of command, NULL if
* the cmd appears as the first character */
TmpPtr, /* Temporary pointer */
ErrPtr, /* Ptr to where an error started */
LineCpy = NULL; /* In some cases, we may wish to strdup() */
BOOL FirstRun, /* To handle solo `\'s as a special case */
ReRun, /* Whether we should read a new character */
OnceMore = TRUE; /* Whether we'll run through the loop again */
UBYTE TmpC, /* Just a temp var used throughout the proc.*/
Char; /* Char. currently processed */
ULONG BrOffset, /* Offset into BrOrder array */
TmpCount, /* Just for misc. counting. */
CmdLen; /* Length of misc. things */
int (*cb)(int c); /* Callback hook */
struct CharInfo *ci;
static const /* Characters we'll do some magic with */
UBYTE MgcChars [] = "$\\{}[]()-^_\"´`\'.";
NEWBUF(Buf, BUFLEN);
if(RealBuf)
{
strcpy(Buf, RealBuf);
TmpPtr = BufPtr = Buf;
/* First, kill comments. */
while(TmpPtr = strchr(TmpPtr, '%'))
{
if(TmpPtr[-1] != '\\')
{
PrintError(RealBuf, TmpPtr - Buf, 1, Line,
emComment);
*TmpPtr = 0;
break;
}
TmpPtr++;
}
/* Then, kill `\verb' commands */
if(WipeVerb)
{
PrePtr = Buf;
while(PrePtr = strstr(PrePtr, "\\verb"))
{
TmpC = PrePtr[5];
if(!isalpha(TmpC) && TmpC)
{
if(TmpPtr = strchr(&PrePtr[6], TmpC))
sfmemset(PrePtr, VERBCLEAR, (LONG) (TmpPtr - PrePtr) + 1);
else
PrintError(RealBuf, PrePtr - Buf, 5, Line,
emNoArgFound,
"\\verb");
}
PrePtr++;
}
}
BufPtr = Buf;
while(OnceMore && (BufPtr = strpbrk(BufPtr, MgcChars)))
{
PrePtr = BufPtr - 1;
Char = *BufPtr++;
do
{
CmdPtr = CmdBuffer;
FirstRun = TRUE;
ReRun = FALSE;
switch(Char)
{
case '.':
case '\'':
case '`':
if((Char == *BufPtr) && (Char == BufPtr[1]))
{
if(Char == '.')
PrintError(RealBuf, BufPtr - Buf - 1, 3, Line,
emEllipsis);
else
PrintError(RealBuf, BufPtr - Buf - 1, 3, Line,
emThreeQuotes,
Char, Char, Char,
Char, Char, Char);
}
break;
case '\"':
PrintError(RealBuf, BufPtr - Buf - 1, 1, Line,
emUseQuoteLiga);
break;
case '´':
PrintError(RealBuf, BufPtr - Buf - 1, 1, Line,
emUseOtherQuote);
break;
case '_':
case '^':
if(*PrePtr != '\\')
{
TmpPtr = PrePtr;
while(TmpC = *TmpPtr--)
{
ifn(LATEX_SPACE(TmpC))
break;
}
CmdLen = 1;
switch(TmpC)
{
/*{*/ case '}':
if(PrePtr[-1] != '\\')
break;
CmdLen++;
PrePtr--;
/* FALLTHRU */
/*[(*/ case ')':
case ']':
case 0:
PrintError(RealBuf, PrePtr - Buf, CmdLen, Line,
emEnclosePar);
break;
}
if(TmpPtr = strip(BufPtr, STRP_LFT))
{
ErrPtr = TmpPtr;
if(isalpha(*TmpPtr))
cb = &isalpha;
elif(isdigit(*TmpPtr))
cb = &isdigit;
else
break;
while((*cb)(*TmpPtr++))
;
TmpPtr--;
if((TmpPtr - ErrPtr) > 1)
PrintError(RealBuf, ErrPtr - Buf, TmpPtr - ErrPtr, Line,
emEmbrace);
}
}
break;
case '-':
ifn(MathMode)
{
for(TmpCount = 1;
*BufPtr++ == '-';
TmpCount++)
;
if( (LATEX_SPACE(*PrePtr) && LATEX_SPACE(*BufPtr) && (TmpCount != 3)) ||
(isdigit(*PrePtr) && isdigit(*BufPtr) && (TmpCount != 2)) ||
(isalpha(*PrePtr) && isalpha(*BufPtr) && (TmpCount != 1)))
{
PrintError(RealBuf, PrePtr - Buf + 1, TmpCount, Line,
emWrongDash);
}
BufPtr--;
}
break;
case '\\': /* Command encountered */
*CmdPtr++ = Char;
*CmdPtr = 0;
while(Char = *BufPtr++)
{
ifn(isalpha(Char) || (AtLetter && (Char == '@')))
{
ifn(FirstRun)
{
if(!MathMode)
{
if(LATEX_SPACE(Char) && (!HasWord(CmdBuffer, &Silent)))
{
PrintError(RealBuf, BufPtr - Buf - 1,
1, Line,
emSpaceTerm);
}
elif((Char == '\\') && (!isalpha(*BufPtr)) && (!LATEX_SPACE(*BufPtr)))
{
PrintError(RealBuf, BufPtr - Buf - 1,
2, Line,
emNotIntended);
}
}
}
else
{
*CmdPtr++ = Char;
*CmdPtr = 0;
Char = *BufPtr++;
}
/* We've now isolated the command */
if(LATEX_SPACE(*PrePtr))
{
if(HasWord(CmdBuffer, &Linker))
PrintError(RealBuf, PrePtr - Buf, 1, Line,
emNBSpace);
if(HasWord(CmdBuffer, &PostLink))
PrintError(RealBuf, PrePtr - Buf, 1, Line,
emFalsePage);
}
if(HasWord(CmdBuffer, &IJAccent))
{
if(TmpPtr = GetLTXArg(&BufPtr[-1], TmpBuffer, '{')) /*}*/
{
if(TmpPtr = strpbrk(TmpPtr, "ij"))
{
if(TmpPtr[-1] != '\\')
PrintError(RealBuf, PrePtr - Buf + 1, (LONG) strlen(CmdBuffer), Line,
emAccent,
CmdBuffer, *TmpPtr, MathMode? "math" : "");
}
}
else
{
PrintError(RealBuf, PrePtr - Buf + 1, (LONG) strlen(CmdBuffer),
Line,
emNoArgFound,
CmdBuffer);
}
}
if(HasWord(CmdBuffer, &Italic))
ItState = itOn;
/*
* Got to do this one here, so we can give error
* position. Should really belong to PerformCommand().
*/
if(!strcmp(CmdBuffer, "\\/"))
{
switch(ItState)
{
case itOn:
ItState = itCorrected;
break;
case itCorrected:
PrintError(RealBuf, PrePtr - Buf + 1, 2, Line,
emItDup);
break;
case itOff:
PrintError(RealBuf, PrePtr - Buf + 1, 2, Line,
emItInNoIt);
}
}
PerformCommand(CmdBuffer);
break;
}
else
{
*CmdPtr++ = Char;
*CmdPtr = 0;
}
FirstRun = FALSE;
}
ReRun = TRUE;
PrePtr = BufPtr - 2;
break;
case '}':
if(*PrePtr != '\\')
{
if(ItState == itOn)
PrintError(RealBuf, PrePtr - Buf + 1, 1, Line,
emNoItFound);
ItState = itOff;
}
/* FALLTHRU */
case '(':
case '[':
case '{':
case ')':
case ']':
AddBracket(Char);
if((BrOffset = BrackIndex(Char)) != ~0UL)
{
if(BrOffset & 1) /* Closing bracket of some sort */
{
if(ci = PopChar(&CharStack))
{
TmpC = MatchBracket(ci->Char);
free(ci);
}
else
TmpC = 0;
if(TmpC != Char)
{
if(TmpC)
PrintError(RealBuf, BufPtr - Buf - 1, 1, Line,
emExpectC,
TmpC, Char);
else
PrintError(RealBuf, BufPtr - Buf - 1, 1, Line,
emSoloC,
Char);
}
}
else /* Opening bracket of some sort */
{
if(!LineCpy)
LineCpy = strdup(RealBuf);
if(LineCpy)
{
ifn(PushChar(Char, Line, BufPtr - Buf, &CharStack, LineCpy))
PrintPrgErr(pmNoStackMem);
}
else
PrintPrgErr(pmStrDupErr);
}
}
break;
case '$':
if(*PrePtr != '\\')
{
if(*BufPtr == '$')
BufPtr++;
MathMode ^= TRUE;
}
break;
default:
ReRun = FALSE;
break;
}
} while(ReRun);
}
/* Search for abbrevs... */
strcpy(TmpBuffer, Buf);
strupr(TmpBuffer);
for(TmpCount = 0L;
TmpCount < Abbrev.Stack.Used;
TmpCount++)
{
for(BufPtr = TmpBuffer;
BufPtr = strstr(BufPtr, Abbrev.Stack.Data[TmpCount]);
BufPtr++)
{
CmdLen = strlen(Abbrev.Stack.Data[TmpCount]);
Char = BufPtr[CmdLen];
if(LATEX_SPACE(Char))
{
PrintError(RealBuf, (BufPtr - TmpBuffer) + CmdLen, 1, Line,
emInterWord);
}
}
}
/* Search for intersentence spacing */
for(BufPtr = Buf;
TmpC = *BufPtr++;
)
{
if(isupper(TmpC) && LATEX_PUNCT(*BufPtr) && (LATEX_SPACE(BufPtr[1])))
{
PrintError(RealBuf, BufPtr - Buf, 1, Line,
emInterSent);
}
}
/* Search for user-specified warnings */
strcpy(TmpBuffer, Buf);
/* strupr(TmpBuffer);
* we'll do things case-sensitive, for now.
*/
for(TmpCount = 0L;
TmpCount < UserWarn.Stack.Used;
TmpCount++)
{
CmdLen = strlen(UserWarn.Stack.Data[TmpCount]);
for(BufPtr = TmpBuffer;
BufPtr = strstr(BufPtr, UserWarn.Stack.Data[TmpCount]);
BufPtr++)
{
PrintError(RealBuf, (BufPtr - TmpBuffer), CmdLen, Line,
emUserWarn);
}
}
}
return(TRUE);
}
/*
* Scans the `Buf' for a LaTeX arg, and puts that arg into `Dest'. `Delim'
* is the leading character for the arg (either `{' or `[', that is).
* Returns NULL if we can't find the argument, ptr to `Dest' in other cases.
*/
STRPTR GetLTXArg(STRPTR Buf, STRPTR Dest, const UBYTE Delim)
{
UBYTE mileD, TmpC;
STRPTR Retval = NULL,
DDest = Dest;
ULONG DeliCnt = 0L,
BufCnt = 0L;
*Dest = 0L;
if(mileD = MatchBracket(Delim))
{
while(TmpC = *Buf++)
{
if(!LATEX_SPACE(TmpC))
break;
}
if(TmpC == Delim)
{
DeliCnt++;
while(DeliCnt > 0L)
{
TmpC = *Buf++;
if(BufCnt++ < (BUFLEN - 1))
*Dest++ = TmpC;
else
break;
if(TmpC == Delim)
DeliCnt++;
elif(TmpC == mileD)
DeliCnt--;
elif(!TmpC)
break;
}
Retval = DDest;
*--Dest = 0L;
}
else
{
Dest[0] = TmpC;
Dest[1] = 0L;
Retval = DDest;
}
}
return(Retval);
}
/*
* Prints and indicates the position of an error. Be sure that `String'
* does not contain tabs, newlines, etc.
*/
#define PRINT_MESSAGE va_start(MsgArgs, Error); \
vfprintf(OutputFile, LaTeXMsgs[Error].Message, MsgArgs); \
va_end(MsgArgs)
void PrintError(const STRPTR String, LONG Position, const LONG Len,
const LONG Line, const enum ErrNum Error, ...)
{
static /* Just to reduce stack usage... */
UBYTE PrintBuffer[BUFLEN];
va_list MsgArgs;
if(betw(emMinFault, Error, emMaxFault) && LaTeXMsgs[Error].InUse)
{
switch(Verbosity)
{
case vbFancy:
case vbNormal:
if(String)
{
strcpy(PrintBuffer, String);
switch(LaTeXMsgs[Error].Type)
{
case etWarn:
fprintf(OutputFile, "Warning %ld in line %ld: ", Error, Line);
WarnPrint++;
break;
case etErr:
fprintf(OutputFile, "Error %ld in line %ld: ", Error, Line);
ErrPrint++;
break;
case etMsg:
fprintf(OutputFile, "Message %ld in line %ld: ", Error, Line);
break;
}
PRINT_MESSAGE;
fprintf(OutputFile, "\n");
if(Verbosity == vbNormal)
{
fprintf(OutputFile, "%s\n", strip(PrintBuffer, STRP_RGT));
sfmemset(PrintBuffer, ' ', (LONG) Position);
sfmemset(&PrintBuffer[Position], '^', Len);
PrintBuffer[Position + Len] = 0;
fprintf(OutputFile, "%s\n", PrintBuffer);
}
else
{
strmid(String, PrintBuffer, 0L, Position);
fputs(PrintBuffer, OutputFile);
strmid(String, PrintBuffer, Position, Len);
ReverseVideo(PrintBuffer, OutputFile);
strmid(String, PrintBuffer, Position + Len, LONG_MAX);
fputs(PrintBuffer, OutputFile);
fprintf(OutputFile, "\n\n");
}
}
else
{
switch(LaTeXMsgs[Error].Type)
{
case etWarn:
fprintf(OutputFile, "General warning %ld: ", Error);
WarnPrint++;
break;
case etErr:
fprintf(OutputFile, "General error %ld: ", Error);
ErrPrint++;
break;
case etMsg:
fprintf(OutputFile, "General message %ld: ", Error);
break;
}
PRINT_MESSAGE;
fprintf(OutputFile, "\n");
}
break;
case vbSilent:
fprintf(OutputFile, "%s%s%ld%s%ld%s%ld%s",
InputName, Delimit,
Line, Delimit,
Position + 1, Delimit,
Error, Delimit);
PRINT_MESSAGE;
fprintf(OutputFile, "\n");
break;
}
}
else
UserSupp++;
}
#undef PRINT_MESSAGE
/*
* All commands isolated is routed through this command, so we can
* update global statuses like math mode and whether @ is a letter
* or not.
*/
void PerformCommand(const STRPTR Cmd)
{
if(!strcmp(Cmd, "\\makeatletter"))
AtLetter = TRUE;
elif(!strcmp(Cmd, "\\makeatother"))
AtLetter = FALSE;
elif(*Cmd == '\\')
{
/* Quicker check of the commands \(, \[, \] and \). */
switch(Cmd[1])
{
case '(':
case '[':
MathMode = TRUE;
break;
case ']':
case ')':
MathMode = FALSE;
break;
}
}
}